import json
from json import JSONDecodeError
from typing import Any, Dict

from django.http import JsonResponse, HttpRequest
from .constants import *


class Result:
    def serialize(self) -> dict:
        return {}


class NulResponse:
    def __init__(self, result: Result, errcode: int = 0, errmsg: str = ''):
        self.version: int = PTL_RESPONSE_VERSION
        self.errcode: int = errcode
        self.errmsg: str = errmsg
        self.result: Result = result

    def serialize(self) -> dict:
        dictionary: dict = {
            'version': self.version,
            'errcode': self.errcode,
            'errmsg': self.errmsg,
            'result': self.result.serialize()
        }
        return dictionary


class NulException(Exception):
    def __init__(self, errcode: int, errmsg: str = ''):
        super().__init__(self)
        self.errcode: int = errcode
        self.errmsg: str = errmsg


def decode_std_body(request: HttpRequest) -> dict:
    try:
        json_object: dict = json.loads(request.body)
    except JSONDecodeError as e:
        raise NulException(ERR_INVALID_JSON_STRING, ERS_INVALID_JSON_STRING)

    version = json_object.get('version')
    if not isinstance(version, int) or version > PTL_REQUEST_VERSION:
        raise NulException(ERR_UNSUPPORTED_API_VERSION, ERS_UNSUPPORTED_API_VERSION)

    return json_object


def get_json_value_or_except(json_obj: dict, key: str, asserted_type: type = Any):
    if key not in json_obj or not isinstance(json_obj[key], asserted_type):
        raise NulException(ERR_INVALID_JSON_STRUCTURE, ERS_INVALID_JSON_STRUCTURE)
    return json_obj[key]


def response(result: Result) -> JsonResponse:
    return JsonResponse(NulResponse(result).serialize())


def response_except(exception: NulException) -> JsonResponse:
    return JsonResponse(NulResponse(Result(), exception.errcode, exception.errmsg).serialize())

